#pragma once
#include<memory>
#include<iostream>
#include<string>
#include<vector>
#include <unistd.h>
#include<cstdio>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
using namespace std;


//创建key
const string SEM_PATH = "./tmp/";
const int SEM_PROJ_ID  = 1234;
//信号量集中信号量默认个数
const int defaultnum = 1;
//flag
#define GET_SEM (IPC_CREAT)
#define BUILD_SEM (IPC_CREAT | IPC_EXCL | 0666)

string intToHex(int num)
{
     char hex[64];
     snprintf(hex,sizeof(hex),"0x%x",num);
     return string(hex);
}

class Semaphore
{
public: 
  Semaphore(int semid)
  :_semid(semid)
  {}

  ~Semaphore()
  {
    if(_semid >= 0) //安全性检查
    {
      int n = ::semctl(_semid,0,IPC_RMID);
      if(n < 0)
      {
        cerr << "semctl ICP_RMID error " << endl;
      }
    }
  }

  void P()
  {
     PV(-1);
  }
  void V()
  {
     PV(1);
  }

private:
     void PV(int data)
     {
        //1.定义结构体
        struct sembuf sem_buf;
        sem_buf.sem_num = 0;//默认信号量只有一个信号量，所以从0开始
        sem_buf.sem_op = data;//P V
        sem_buf.sem_flg = SEM_UNDO;//不关心
        //2.调用
        int n = ::semop(_semid,&sem_buf,1);//对一个信号量操作
        if(n < 0)
        {
            cerr << "semop error" << endl;
        }
     }  

private:
   int _semid;
};

//信号量建造者
class SemBuilderBuilder
{
public:
      SemBuilderBuilder(){}
      ~SemBuilderBuilder(){}
     std::shared_ptr<Semaphore> build(int flag,int num = defaultnum) //构建信号量集(flg,信号量个数)
     {
        //1.获取key值
        key_t key = ::ftok(SEM_PATH.c_str(),SEM_PROJ_ID);
        if(key == -1)
        {
            cerr << "ftok error " << endl;
            return nullptr;
        }
        cout << "Get Key Success: " << intToHex(key) << endl;
        //2.创建信号量：
        int semid = ::semget(key,num,flag);
        if(semid == -1)
        {
            cerr << "create sem error " << endl;
            return nullptr;
        }
        cout << "create Sem success: " << semid << endl;
        //3.初始化 创建的人才用初始化
        if(flag == BUILD_SEM)
          Init(semid,0); //这里我们默认信号胡中只有一个信号量
        return make_shared<Semaphore>(semid);

     }

     SemBuilderBuilder& SetVal(int val)
     {
        _val = val;
        return *this;
     }

private:
    bool Init(int semid,int num) //哪个信号量集 哪个信号量
    {
        //1.自定义联合体对象
        union semun {
            int              val;    /* Value for SETVAL */
            struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
            unsigned short  *array;  /* Array for GETALL, SETALL */
            struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                        (Linux-specific) */
        }un;
        un.val = _val;
        //2.调用semctl
        int n = ::semctl(semid,num,SETVAL,un);
        if(n < 0)
        {
            cerr << "semctl SETVAL error " << endl;
            return false;
        } 
        return true;
    }
private:
     int _val ;    

};